#ifndef __MYSOCK_HPP__
#define __MYSOCK_HPP__

// 网络套接字封装
#include <sys/types.h>
#include <sys/socket.h>
#include <string>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <unistd.h>
#include <fcntl.h>

namespace QiHai
{
    class MySock
    {
    public:
        // 创建基于TCP的套接字对象，使用IPV4协议通信
        // 返回一个套接字文件描述符，-1则出错
        static int Socket()
        {
            return socket(AF_INET, SOCK_STREAM, 0);
        }

        // 绑定IP和端口
        static bool Bind(int sock, const std::string& ip, uint16_t port)
        {
            // 一般使用bind是服务器，所以，这里设置服务器主动取消四次挥手后的等待time_wait状态
            int opt = 1;  // 表示打开
            setsockopt(sock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof opt);  // 在绑定前设置好

            struct sockaddr_in server;
            bzero(&server, 0);
            server.sin_family = AF_INET;
            server.sin_port = htons(port);
            // server.sin_addr.s_addr = inet_addr(ip.c_str());
            inet_aton(ip.c_str(), &server.sin_addr);

            return (-1 != bind(sock, (sockaddr*)&server, sizeof server));
        }

        // Server设置监听套接字 连接队列默认20
        static bool ListenSock(int sock, int n = 20)
        {
            return (-1 != listen(sock, n));
        }

        // Server从连接队列中提取连接
        // 返回一个此连接的服务套接字文件描述符，-1则为错误 错误可以提供错误码，默认置空
        static int Accept(int listenSock, std::pair<std::string, uint16_t>* clientData, int* errNum = nullptr)
        {
            struct sockaddr_in client;
            bzero(&client, 0);
            socklen_t client_len = sizeof client;

            int sock = accept(listenSock, (sockaddr*)&client, &client_len);
            if (sock < 0) return sock;
            if (clientData != nullptr)
            {
                clientData->first = inet_ntoa(client.sin_addr);
                clientData->second = ntohs(client.sin_port);
            }
            if (errNum != nullptr) *errNum = errno;
            return sock;
        }

        // client连接Server
        static bool Connect(int sock, const std::string& serverIp, std::uint16_t serverPort)
        {
            struct sockaddr_in server;
            bzero(&server, 0);
            server.sin_family = AF_INET;
            server.sin_addr.s_addr = inet_addr(serverIp.c_str());
            server.sin_port = htons(serverPort);

            return (-1 != connect(sock, (sockaddr*)&server, sizeof server));
        }

        // 设置sock为非阻塞模式
        static bool IsNoBlock(int sock)
        {
            int fl = fcntl(sock, F_GETFL);  // 获取sock文件描述符原本的状态-历史状态保留
            if (fl < 0) return false;
            return (-1 != fcntl(sock, F_SETFL, fl | O_NONBLOCK));
        }
    };
}

#endif